////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxpipeline.cpp
/// @brief Pipeline class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxatomic.h"
#include "camxdebug.h"
#include "camxthreadmanager.h"
#include "camxhwcontext.h"
#include "camxhwdefs.h"
#include "camxhwfactory.h"
#include "camximagesensormoduledatamanager.h"
#include "camximagesensormoduledata.h"
#include "camximagesensordata.h"
#include "camxmem.h"
#include "camxmetadatapool.h"
#include "camxncsservice.h"
#include "camxnode.h"
#include "camxpipeline.h"
#include "camxsession.h"
#include "camxtrace.h"
#include "camxvendortags.h"
#include "camxchicontext.h"
#include "camxhal3metadatautil.h"
#include "camxstatsdebugdatatypes.h"
#include "camxtypes.h"

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::Pipeline
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pipeline::Pipeline()
{
    m_currentCreateStatus = PipelineCreateStatus::UNINITIALIZED;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::~Pipeline
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Pipeline::~Pipeline()
{
    NCSService* phNCSServiceHandle = NULL;
    CamxResult  result             = CamxResultSuccess;

    DestroyNodes();

    m_currentCreateStatus = PipelineCreateStatus::UNINITIALIZED;

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::Create(
    PipelineCreateInputData*  pCreateInputData,
    PipelineCreateOutputData* pCreateOutputData)
{
    /// @todo (CAMX-1512) Add pipeline event
    CAMX_ENTRYEXIT_SCOPE(CamxLogGroupCore, SCOPEEventTopologyCreate);

    CAMX_ASSERT((NULL != pCreateInputData) && (NULL != pCreateOutputData));

    // 创建Pipeline
    CamxResult result    = CamxResultSuccess;
    Pipeline*  pPipeline = CAMX_NEW Pipeline;

    if (NULL != pPipeline)
    {
        // 初始化pPipeline
        result = pPipeline->Initialize(pCreateInputData, pCreateOutputData);

        if (CamxResultSuccess == result)
        {
            pCreateOutputData->pPipeline = pPipeline;
        }
        else
        {
            pCreateOutputData->pPipeline = NULL;
            pPipeline->Destroy();
            pPipeline = NULL;
        }
    }
    else
    {
        CAMX_ASSERT_ALWAYS_MESSAGE("Out of memory");
        result = CamxResultENoMemory;
    }

    CAMX_ASSERT(CamxResultSuccess == result);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::Destroy
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::Destroy()
{

    CAMX_ENTRYEXIT_SCOPE(CamxLogGroupCore, SCOPEEventTopologyDestroy);
    if (NULL != m_pConfigDoneLock)
    {
        m_pConfigDoneLock->Destroy();
        m_pConfigDoneLock = NULL;
    }

    if (NULL != m_pWaitForConfigDone)
    {
        m_pWaitForConfigDone->Destroy();
        m_pWaitForConfigDone = NULL;
    }

    if (NULL != m_pInputPool)
    {
        m_pInputPool->Destroy();
        m_pInputPool = NULL;
    }

    if (NULL != m_pMainPool)
    {
        m_pMainPool->Destroy();
        m_pMainPool = NULL;
    }

    if (NULL != m_pEarlyMainPool)
    {
        m_pEarlyMainPool->Destroy();
        m_pEarlyMainPool = NULL;
    }

    if (NULL != m_pInternalPool)
    {
        m_pInternalPool->Destroy();
        m_pInternalPool = NULL;
    }

    if (NULL != m_pUsecasePool)
    {
        m_pUsecasePool->Destroy();
        m_pUsecasePool = NULL;
    }

    if (NULL != m_pEmptyMetaDataPool)
    {
        m_pEmptyMetaDataPool->Destroy();
        m_pEmptyMetaDataPool = NULL;
    }

    if (NULL != m_pPerRequestInfoLock)
    {
        m_pPerRequestInfoLock->Destroy();
        m_pPerRequestInfoLock = NULL;
    }

    CAMX_DELETE this;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::Initialize(
    PipelineCreateInputData*  pPipelineCreateInputData,
    PipelineCreateOutputData* pPipelineCreateOutputData)
{
    CamxResult result = CamxResultEFailed;

    // 给pipeline的成员变量赋值
    m_pChiContext                   = pPipelineCreateInputData->pChiContext;
    m_flags.isSecureMode            = pPipelineCreateInputData->isSecureMode;
    m_flags.isHFRMode               = pPipelineCreateInputData->pPipelineDescriptor->flags.isHFRMode;
    m_flags.isInitialConfigPending  = TRUE;
    m_pThreadManager                = pPipelineCreateInputData->pChiContext->GetThreadManager();
    m_pPipelineDescriptor           = pPipelineCreateInputData->pPipelineDescriptor;
    m_pipelineIndex                 = pPipelineCreateInputData->pipelineIndex;
    m_cameraId                      = m_pPipelineDescriptor->cameraId;
    m_hCSLLinkHandle                = CSLInvalidHandle;
    m_numConfigDoneNodes            = 0;
    m_lastRequestId                 = 0;
    m_configDoneCount               = 0;
    m_partialStreamon               = FALSE;
    m_hCSLLinkHandle                = 0;

    // Create lock and condition for config done
    m_pConfigDoneLock       = Mutex::Create("PipelineConfigDoneLock");
    m_pWaitForConfigDone    = Condition::Create("PipelineWaitForConfigDone");

    CAMX_ASSERT(NULL != m_pConfigDoneLock);
    CAMX_ASSERT(NULL != m_pWaitForConfigDone);

    // We can't defer UsecasePool since we are publishing preview dimension to it.
    // TODO
    m_pUsecasePool  = MetadataPool::Create(PoolType::PerUsecase, m_pipelineIndex, NULL, 1);

    if (NULL != m_pUsecasePool)
    {
        // 更新m_slotRequestId
        m_pUsecasePool->UpdateRequestId(0); // Usecase pool created, mark the slot as valid
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Out of memory!!");
        return CamxResultENoMemory;
    }

    QueryEISCaps();

    if (TRUE == IsRealTime())
    {
        // 打印输出
        PublishOutputDimensions();
        // 打印targetFPS
        PublishTargetFPS();
    }

    // 修改MetadataSlot的numBatchedFrames和maxFPSValue
    SetNumBatchedFrames(m_pPipelineDescriptor->numBatchedFrames, m_pPipelineDescriptor->maxFPSValue);
    // TODO
    result = CreateNodes(pPipelineCreateInputData, pPipelineCreateOutputData);

    if (CamxResultSuccess == result)
    {
        m_pPerRequestInfoLock = Mutex::Create("PipelineRequestInfo");
        if (NULL == m_pPerRequestInfoLock)
        {
            result = CamxResultENoMemory;
        }
    }

    if (CamxResultSuccess == result)
    {
        pPipelineCreateOutputData->pPipeline = this;
        m_currentCreateStatus = PipelineCreateStatus::INITIALIZED;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::InitializeMetadataPools
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::InitializeMetadataPools(
    UINT numSlots)
{
    // Create perFrame MetadataPools for pipeline
    m_pMainPool         = MetadataPool::Create(PoolType::PerFrameResult, m_pipelineIndex, m_pThreadManager, numSlots);
    m_pEarlyMainPool    = MetadataPool::Create(PoolType::PerFrameResultEarly, m_pipelineIndex, m_pThreadManager, numSlots);
    m_pInputPool        = MetadataPool::Create(PoolType::PerFrameInput, m_pipelineIndex, m_pThreadManager, numSlots);
    m_pInternalPool     = MetadataPool::Create(PoolType::PerFrameInternal, m_pipelineIndex, m_pThreadManager, numSlots);
    m_pEmptyMetaDataPool= MetadataPool::Create(PoolType::PerFrameEmptyMetadata, m_pipelineIndex, NULL, numSlots);

    CAMX_ASSERT(NULL != m_pMainPool);
    CAMX_ASSERT(NULL != m_pEarlyMainPool);
    CAMX_ASSERT(NULL != m_pInputPool);
    CAMX_ASSERT(NULL != m_pInternalPool);
    CAMX_ASSERT(NULL != m_pEmptyMetaDataPool);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::PrepareStreamOn
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::PrepareStreamOn()
{
    CamxResult result = CamxResultSuccess;

    UINT devicecount = 0;

    for (UINT i = 0; i < m_nodeCount; i++)
    {
        if (NULL != m_ppNodes[i])
        {
            result = m_ppNodes[i]->PrepareStreamOn();

            if (CamxResultSuccess != result)
            {
                break;
            }

            for (UINT j = 0; j < m_ppNodes[i]->CSLDeviceHandleCount(); j++)
            {
                m_hDevices[devicecount++] =  m_ppNodes[i]->GetCSLDeviceHandle(j);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::StreamOn
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::StreamOn()
{
    CamxResult result = CamxResultSuccess;

    // 如果不在流程中 || 部分在流程中
    if (FALSE == IsStreamedOn() || TRUE == m_partialStreamon)
    {
        // 上次无变化时间戳
        m_lastMonoTimestamp   = 0;
        // Last QTimer timestamp
        m_lastQTimerTimestamp = 0;

        // 如果管道为实时的
        if (IsRealTime())
        {
            // Wait for realtime pipeline config done before calling stream on
            // 在调用流之前，请等待实时管道配置完成
            m_pConfigDoneLock->Lock();
            while (m_configDoneCount != (m_pSession->GetCurrentPipelineRequestId(m_pipelineIndex) - m_lastRequestId))
            {
                CAMX_LOG_INFO(CamxLogGroupCore,
                              "Wait before Stream on for %p current pipeline request %llu, last request id %llu",
                              this,
                              m_pSession->GetCurrentPipelineRequestId(m_pipelineIndex),
                              m_lastRequestId);
                m_pWaitForConfigDone->Wait(m_pConfigDoneLock->GetNativeHandle());
            }
            m_pConfigDoneLock->Unlock();
        }

        CAMX_LOG_INFO(CamxLogGroupCore, "StreamingOn for pipeline: %p", this);
        // 将m_isStreamedOn设置为true
        StreamedOn(TRUE);
        result = m_pChiContext->GetHwContext()->StreamOn(GetCSLLink(), GetCSLDevices());
        m_partialStreamon = FALSE;
    }

    // 若m_pChiContext->GetHwContext()->StreamOn(GetCSLLink(), GetCSLDevices())执行失败，则设置m_isStreamedOn为false
    if (CamxResultSuccess != result)
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Failed to StreamOn error: %d", result);
        StreamedOn(FALSE);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::StreamOff
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::StreamOff(
    CHIDEACTIVATEPIPELINEMODE modeBitmask)
{
    CamxResult result = CamxResultSuccess;

    if (m_ppNodes != NULL)
    {
        // StreamOff each node in pipeline
        for (UINT i = 0; i < m_nodeCount; i++)
        {
            if (NULL != m_ppNodes[i])
            {
                result = m_ppNodes[i]->PrepareStreamOff(modeBitmask);
            }
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "%s PrepareStreamOff failed.", m_ppNodes[i]->Name());
                break;
            }
        }
    }

    // ChiContext StreamOff is only called if defaultDeactivateMode or sensorStandbyMode bit is set
    if (CamxResultSuccess == result &&
        ((modeBitmask & CHIDeactivateModeDefault) ||
        (modeBitmask & CHIDeactivateModeSensorStandby)))
    {
        if (TRUE == IsStreamedOn() || TRUE == m_partialStreamon)
        {
            CAMX_LOG_INFO(CamxLogGroupCore, "StreamingOff for pipeline: %p", this);

            result = m_pChiContext->GetHwContext()->StreamOff(GetCSLLink(), GetCSLDevices(), modeBitmask);
            if (CamxResultSuccess == result)
            {
                m_configDoneCount               = 0;
                m_flags.isInitialConfigPending  = TRUE;
                m_lastRequestId                 = m_pSession->GetCurrentPipelineRequestId(m_pipelineIndex);
                CAMX_LOG_INFO(CamxLogGroupCore,
                    "Streaming Off for pipeline successful: %p, last request id %llu is real time %d cam Id: %d mode : %d",
                    this,
                    m_lastRequestId,
                    IsRealTime(), m_cameraId, modeBitmask);
                StreamedOn(FALSE);
                if (modeBitmask & CHIDeactivateModeSensorStandby)
                {
                    m_partialStreamon = TRUE;
                }
                else
                {
                    m_partialStreamon = FALSE;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "StreamingOff for pipeline: %p Failed", this);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::Link
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::Link()
{
    CamxResult result = CamxResultSuccess;

    if (TRUE == IsRealTime())
    {
        result = m_pChiContext->GetHwContext()->Link(GetCSLLink(), GetCSLDevices());
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "Link for pipeline: %p Falied", this);
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::Unlink
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::Unlink()
{
    CamxResult result = CamxResultSuccess;

    if ((TRUE == IsRealTime()) && (NULL != GetCSLLink()) && (0 != *GetCSLLink()))
    {
        result = m_pChiContext->GetHwContext()->Unlink(GetCSLLink(), GetCSLDevices());
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "Unlink for pipeline: %p Failed. Link = %p", this, GetCSLLink());
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::GetDualCameraSyncEnabled
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::GetDualCameraSyncEnabled(
    UINT64 requestId)
{
    UINT32  tag                 = 0;
    BOOL    isSyncModeEnabled   = FALSE;

    // Query the kernel frame sync mode to sync both the real time requests
    if (CDKResultSuccess == VendorTagManager::QueryVendorTagLocation("com.qti.chi.multicamerainfo", "SyncMode", &tag))
    {
        SyncModeInfo* pInputMetadata = NULL;
        MetadataSlot* pMetadataSlot = m_pInputPool->GetSlot(requestId);
        pMetadataSlot->GetMetadataByTag(InputMetadataSectionMask | tag, reinterpret_cast<VOID**>(&pInputMetadata));

        if (NULL != pInputMetadata)
        {
            isSyncModeEnabled = pInputMetadata->isSyncModeEnabled;
        }
        CAMX_LOG_INFO(CamxLogGroupCore, "VendorTag syncmode enabled %d", isSyncModeEnabled);
    }
    return isSyncModeEnabled;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::OpenRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::OpenRequest(
    UINT64 requestId,
    UINT64 CSLSyncID)
{
    CamxResult result = CamxResultSuccess;

    if (TRUE == IsRealTime())
    {
        // First PCR before stream on will be an init op, do not call CSLOpenRequest on this request.
        // isInitialConfigPending flag is set to true on pipeline init and stream off
        if ((TRUE == m_flags.isInitialConfigPending) && (FALSE == IsStreamedOn()))
        {
            m_flags.isInitialConfigPending = FALSE;
        }
        else
        {
            if (TRUE == GetDualCameraSyncEnabled(requestId))
            {
                CAMX_LOG_INFO(CamxLogGroupCore, "pipeline[%d] CSLOpenRequest for CSLSyncID: %llu, requestId: %llu with"
                    " syncmode true",
                    m_pipelineIndex, CSLSyncID, requestId);
                result = CSLOpenRequest(m_hCSLSession, m_hCSLLinkHandle, CSLSyncID, FALSE, CSLSyncLinkModeSync);
            }
            else
            {
                CAMX_LOG_INFO(CamxLogGroupCore, "pipeline[%d] CSLOpenRequest for CSLSyncID: %llu, requestId: %llu with"
                    " syncmode false",
                    m_pipelineIndex, CSLSyncID, requestId);
                result = CSLOpenRequest(m_hCSLSession, m_hCSLLinkHandle, CSLSyncID, FALSE, CSLSyncLinkModeNoSync);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::ProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// struct PipelineProcessRequestData
// {
//     CaptureRequest*     pCaptureRequest;                        ///< Current capture request
//     PerBatchedFrameInfo perBatchedFrameInfo[MaxBatchedFrames];  ///< Information about all batched frames
// };
CamxResult Pipeline::ProcessRequest(
    PipelineProcessRequestData* pPipelineRequestData)
{
    CAMX_ASSERT(NULL != pPipelineRequestData);
    CAMX_ASSERT(NULL != pPipelineRequestData->pCaptureRequest);

    CAMX_ENTRYEXIT_SCOPE_ID(CamxLogGroupCore,
                            SCOPEEventTopologyProcessRequest,
                            pPipelineRequestData->pCaptureRequest->requestId);

    CamxResult      result                    = CamxResultSuccess;
    
//     struct CaptureRequest
// {
//     UINT                   numBatchedFrames;                      ///< Number of frames batched in this request
//     UINT64                 requestId;                             ///< RequestId of the (possibly batched) request
//     UINT64                 CSLSyncID;                             ///< syncID to synchronize csl Links
//     const Metadata*        pMetadata;                             ///< Metadata of the (possibly batched) request
//     StreamBufferInfo       streamBuffers[MaxBatchedFrames];       ///< Input buffer of the (possibly batched) request
//     UINT                   pipelineIndex;                         ///< Index of pipeline which this PCR belongs to
//     CbPrivateData*         pPrivData;                             ///< Private data for callback
//     MultiRequestSyncData*  pMultiRequestData;                     ///< Multi request sync data pointer
// };

    CaptureRequest* pCaptureRequest           = pPipelineRequestData->pCaptureRequest;
    UINT            currentActiveStreamIdMask = 0;
    UINT64          requestId                 = pCaptureRequest->requestId;
    UINT            perRequestIdIndex         = (requestId % MaxPerRequestInfo);

    // Must have recieved all fences created back and freed their node in the LDLL
    CAMX_ASSERT(0 == m_perRequestInfo[perRequestIdIndex].fences.NumNodes());

//     struct PerRequestInfo
// {
//     UINT                        aMetadataReady;                    ///< 1, one of SOF or numNodesMetadataDone == numNodes done
//                                                                    ///  0, both remain
//     UINT                        numNodesRequestIdDone;             ///< Number of nodes that are done processing
//     UINT                        numNodesMetadataDone;              ///< Number of nodes that are done generating metadata
//     UINT                        numNodesConfigDone;                ///< Number of nodes that are done config
//     UINT32                      sequenceId[MaxBatchedFrames];      ///< Sequence ID mapped to each batch of the request
//     BOOL                        isSensorTimestampValid;            ///< Flag for valid sensor time stamp value has been set
//     UINT64                      batchFrameIntervalNanoSeconds;     ///< batch frame interval for HFR usecase
//     CaptureRequest              request;                           ///< Copy of request from session
//     LightweightDoublyLinkedList fences;                            ///< Fences registered for request
// };

    Utils::Memset(&m_perRequestInfo[perRequestIdIndex], 0, sizeof(PerRequestInfo));

    Utils::Memcpy(&m_perRequestInfo[perRequestIdIndex].request, pCaptureRequest, sizeof(CaptureRequest));

    // 更新请求索引
    if (NULL != pCaptureRequest->pPrivData)
    {
        pCaptureRequest->pPrivData->requestIndex = m_pipelineIndex;
    }

    for (UINT batchIndex = 0; batchIndex < pCaptureRequest->numBatchedFrames; batchIndex++)
    {
        // Create mapping between request id and framework frame number.
        m_perRequestInfo[perRequestIdIndex].sequenceId[batchIndex] =
            pPipelineRequestData->perBatchedFrameInfo[batchIndex].sequenceId;

        CAMX_LOG_VERBOSE(CamxLogGroupCore,
                         "In perRequestInfo[%d], map request id %lld with sequence id %d",
                         perRequestIdIndex,
                         requestId,
                         pPipelineRequestData->perBatchedFrameInfo[batchIndex].sequenceId);
    }

    /// @note It is assumed the first batchInfo will have all the streams that can possibly be enabled in other batches

//     struct PerBatchedFrameInfo
// {
//     UINT32          sequenceId;                             ///< Framework frame number
//     UINT            activeStreamIdMask;                     ///< Active Stream Id Mask
//     BufferHandle*   phBuffers[MaxNumStreams];               ///< Native Handles corresponding to activeStreamIds[index];
//                                                             ///  Buffers for the active streams indexed with streamId
// };
    PerBatchedFrameInfo* pPerBatchedFrameInfo = &pPipelineRequestData->perBatchedFrameInfo[0];
#if ASSERTS_ENABLED
    /// Validate assumption that the first batchInfo will have all the streams that can possibly be enabled
    for (UINT batchIndex = 1; batchIndex < pCaptureRequest->numBatchedFrames; batchIndex++)
    {
        if ((pPerBatchedFrameInfo[batchIndex].activeStreamIdMask | pPerBatchedFrameInfo[0].activeStreamIdMask) !=
            pPerBatchedFrameInfo[0].activeStreamIdMask)
        {
            CAMX_ASSERT_ALWAYS_MESSAGE("Pipeline-ERROR: First batch index does not have all the streams possible");
        }
    }
#endif // ASSERTS_ENABLED

    // 实际上获取的是pPerBatchedFrameInfo[0].activeStreamIdMask
    currentActiveStreamIdMask = pPerBatchedFrameInfo->activeStreamIdMask;


    UINT* pDifferentActiveStreams = NULL;
    
    if (m_lastRequestActiveStreamIdMask != currentActiveStreamIdMask)
    {
        pDifferentActiveStreams = &currentActiveStreamIdMask;
    }

    if (TRUE == m_pChiContext->GetHwContext()->GetImageSensorModuleData(m_cameraId)->IsExternalSensor())
    {
        PublishSensorModeInformation(pCaptureRequest->requestId);
    }

    // Init debug/tuning data buffer
    InitializeDebugDataBuffer(requestId);

    // Use this table to find the correct request ID from CSL sync ID when CSL callback comes
    m_CSLSyncIDToRequestId[pCaptureRequest->CSLSyncID % (MaxPerRequestInfo * MaxBatchedFrames)] = pCaptureRequest->requestId;

    if (FALSE == IsRealTime())
    {
        UINT64        requestIdInner = pCaptureRequest->requestId;
        UINT64*       pTimestamp     = NULL;
        MetadataSlot* pMetadataSlot  = m_pInputPool->GetSlot(requestIdInner);
        UINT32        tag            = SensorTimestamp;

        pMetadataSlot->GetMetadataByTag(InputMetadataSectionMask | tag, reinterpret_cast<VOID**>(&pTimestamp));

        pMetadataSlot = m_pMainPool->GetSlot(requestIdInner);

        pMetadataSlot->WriteLock();

        pMetadataSlot->SetMetadataByTag(tag, pTimestamp, 1);
        pMetadataSlot->PublishMetadataList(&tag, 1);

        pMetadataSlot->Unlock();
        SendOfflineShutterNotification(requestIdInner, pTimestamp);
    }

    // For PCRs with input buffer(s), shall we assume that the HAL have successfully waited on (all) the input
    // acquire fence(s) before forwarding such a request to the topology? If YES, the topology need not wait on
    // any fences on the SourceBuffer nodes. If NO, then the fence will have to be waited on.

    // Since CSLCreateNativeFence is not implemented, it is expected that a CSLFence is created and passed on
    // with each Input Buffer of an Offline/Reprocess PCR

    if (CamxResultSuccess == result)
    {
        CAMX_ASSERT_MESSAGE(32 >= m_nodeCount, "Using a 32 bit bitmask to track node enable, and there are too many nodes");

        UINT32 nodesEnabled = 0;

        for (UINT i = 0; i < m_nodeCount ; i++)
        {
            BOOL isNodeEnabled = FALSE;

            // TODO
            m_ppNodes[i]->SetupRequest(pPerBatchedFrameInfo,
                                       pDifferentActiveStreams,
                                       requestId,
                                       pCaptureRequest->CSLSyncID,
                                       &isNodeEnabled);

            if (TRUE == isNodeEnabled)
            {
                nodesEnabled = Utils::BitSet(nodesEnabled, i);
            }
        }

        // Queueing the nodes is deferred to ensure all nodes have completed setup before any are invoked, which could happen
        // in the event of a previous request kicking the queue
        for (UINT i = 0; i < m_nodeCount ; i++)
        {
            if (TRUE == Utils::IsBitSet(nodesEnabled, i))
            {
                CAMX_LOG_DRQ("Queueing Node: %s on pipeline: %d for new requestId: %llu",
                    m_ppNodes[i]->Name(), m_pipelineIndex, requestId);

                result = m_pDeferredRequestQueue->AddDeferredNode(requestId,
                                                                  m_ppNodes[i],
                                                                  NULL);
            }
            else
            {
                CAMX_LOG_DRQ("Skipping Node: %s on pipeline: %d for new requestId: %llu",
                    m_ppNodes[i]->Name(), m_pipelineIndex, requestId);
            }
        }

        m_lastRequestActiveStreamIdMask = currentActiveStreamIdMask;

        // Consider any nodes now ready
        m_pDeferredRequestQueue->DispatchReadyNodes();
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::CreateNodes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::CreateNodes(
    PipelineCreateInputData*  pCreateInputData,
    PipelineCreateOutputData* pCreateOutputData)
{
    /// @todo (CAMX-423) Break it into smaller functions

    CAMX_UNREFERENCED_PARAM(pCreateOutputData);

    CamxResult                result                    = CamxResultSuccess;
    const PipelineDescriptor* pPipelineDescriptor       = pCreateInputData->pPipelineDescriptor;
    const PerPipelineInfo*    pPipelineInfo             = &pPipelineDescriptor->pipelineInfo;
    UINT                      numInPlaceSinkBufferNodes = 0;
    Node*                     pInplaceSinkBufferNode[MaxNodeType];
    UINT                      numBypassableNodes        = 0;
    Node*                     pBypassableNodes[MaxNodeType];
    ExternalComponentInfo*    pExternalComponentInfo    = HwEnvironment::GetInstance()->GetExternalComponent();
    UINT                      numExternalComponents     = HwEnvironment::GetInstance()->GetNumExternalComponent();

    CAMX_ASSERT(NULL == m_ppNodes);

    m_nodeCount                        = pPipelineInfo->numNodes;
    m_ppNodes                          = static_cast<Node**>(CAMX_CALLOC(sizeof(Node*) * m_nodeCount));
    m_ppNodesFinalizeNegotiationOutput = static_cast<Node**>(CAMX_CALLOC(sizeof(Node*) * m_nodeCount));

    CAMX_ASSERT(NULL != m_ppNodesFinalizeNegotiationOutput);

    if ((NULL != m_ppNodes) &&
        (NULL != m_ppNodesFinalizeNegotiationOutput))
    {
        NodeCreateInputData createInputData  = { 0 };

        createInputData.pPipeline    = this;
        createInputData.pChiContext  = pCreateInputData->pChiContext;

        UINT nodeIndex = 0;

        CAMX_LOG_INFO(CamxLogGroupCore,
                      "Topology: Creating Pipeline, numNodes %d isSensorInput %d isRealTime %d",
                      m_nodeCount,
                      IsSensorInput(),
                      IsRealTime());

        for (UINT numNodes = 0; numNodes < m_nodeCount; numNodes++)
        {
            NodeCreateOutputData createOutputData = { 0 };
            createInputData.pNodeInfo         = &(pPipelineInfo->nodeInfo[numNodes]);
            createInputData.pipelineNodeIndex = numNodes;

            for (UINT propertyIndex = 0; propertyIndex < createInputData.pNodeInfo->nodePropertyCount; propertyIndex++)
            {
                for (UINT index = 0; index < numExternalComponents; index++)
                {
                    if ((pExternalComponentInfo[index].nodeAlgoType == ExternalComponentNodeAlgo::COMPONENTALGORITHM) &&
                        (NodePropertyCustomLib == createInputData.pNodeInfo->pNodeProperties[propertyIndex].id))
                    {
                        CHAR matchString[FILENAME_MAX] = {0};
                        OsUtils::SNPrintF(matchString, FILENAME_MAX, "%s.%s",
                            static_cast<CHAR*>(createInputData.pNodeInfo->pNodeProperties[propertyIndex].pValue),
                            SharedLibraryExtension);

                        if (OsUtils::StrNICmp(pExternalComponentInfo[index].pComponentName,
                            matchString,
                            OsUtils::StrLen(pExternalComponentInfo[index].pComponentName)) == 0)
                        {
                            if (pExternalComponentInfo[index].statsAlgo == ExternalComponentStatsAlgo::ALGOAF)
                            {
                                createInputData.pAFAlgoCallbacks = &pExternalComponentInfo[index].AFAlgoCallbacks;
                            }
                            else if (pExternalComponentInfo[index].statsAlgo == ExternalComponentStatsAlgo::ALGOAEC)
                            {
                                createInputData.pAECAlgoCallbacks = &pExternalComponentInfo[index].AECAlgoCallbacks;
                            }
                            else if (pExternalComponentInfo[index].statsAlgo == ExternalComponentStatsAlgo::ALGOAWB)
                            {
                                createInputData.pAWBAlgoCallbacks = &pExternalComponentInfo[index].AWBAlgoCallbacks;
                            }
                            else if (pExternalComponentInfo[index].statsAlgo == ExternalComponentStatsAlgo::ALGOAFD)
                            {
                                createInputData.pAFDAlgoCallbacks = &pExternalComponentInfo[index].AFDAlgoCallbacks;
                            }
                            else if (pExternalComponentInfo[index].statsAlgo == ExternalComponentStatsAlgo::ALGOASD)
                            {
                                createInputData.pASDAlgoCallbacks = &pExternalComponentInfo[index].ASDAlgoCallbacks;
                            }
                            else if (pExternalComponentInfo[index].statsAlgo == ExternalComponentStatsAlgo::ALGOPD)
                            {
                                createInputData.pPDLibCallbacks = &pExternalComponentInfo[index].PDLibCallbacks;
                            }
                        }
                    }
                    else if ((pExternalComponentInfo[index].nodeAlgoType == ExternalComponentNodeAlgo::COMPONENTHVX) &&
                        (NodePropertyCustomLib == createInputData.pNodeInfo->pNodeProperties[propertyIndex].id) &&
                        (OsUtils::StrStr(pExternalComponentInfo[index].pComponentName,
                        static_cast<CHAR*>(createInputData.pNodeInfo->pNodeProperties[propertyIndex].pValue)) != NULL))
                    {
                        createInputData.pHVXAlgoCallbacks = &pExternalComponentInfo[index].HVXAlgoCallbacks;
                    }

                }
            }

            result = Node::Create(&createInputData, &createOutputData);

            if (CamxResultSuccess == result)
            {
                CAMX_LOG_INFO(CamxLogGroupCore,
                              "Topology: Node:InstanceId %s:%d Type %d numInputPorts %d numOutputPorts %d",
                              createOutputData.pNode->Name(),
                              createOutputData.pNode->InstanceID(),
                              createOutputData.pNode->Type(),
                              createInputData.pNodeInfo->inputPorts.numPorts,
                              createInputData.pNodeInfo->outputPorts.numPorts);

                if (StatsProcessing == createOutputData.pNode->Type())
                {
                    m_flags.hasStatsNode = TRUE;
                }

                m_ppNodes[nodeIndex] = createOutputData.pNode;

                if ((TRUE == createOutputData.createFlags.isSinkBuffer) ||
                    (TRUE == createOutputData.createFlags.isSinkNoBuffer))
                {
                    m_nodesSinkOutPorts.nodeIndices[m_nodesSinkOutPorts.numNodes] = nodeIndex;
                    m_nodesSinkOutPorts.numNodes++;
                }

                if ((TRUE == createOutputData.createFlags.isSinkBuffer) && (TRUE == createOutputData.createFlags.isInPlace))
                {
                    pInplaceSinkBufferNode[numInPlaceSinkBufferNodes] = createOutputData.pNode;
                    numInPlaceSinkBufferNodes++;
                }

                if (TRUE == createOutputData.createFlags.isBypassable)
                {
                    pBypassableNodes[numBypassableNodes] = createOutputData.pNode;
                    numBypassableNodes++;
                }

                if ((TRUE == createOutputData.createFlags.isSourceBuffer) || (Sensor == m_ppNodes[nodeIndex]->Type()))
                {
                    m_nodesSourceInPorts.nodeIndices[m_nodesSourceInPorts.numNodes] = nodeIndex;
                    m_nodesSourceInPorts.numNodes++;
                }

                if (TRUE == createOutputData.createFlags.willNotifyConfigDone)
                {
                    m_numConfigDoneNodes++;
                }

                nodeIndex++;
            }
            else
            {
                break;
            }
        }

        if (CamxResultSuccess == result)
        {
            // Set the input link of the nodes - basically connects output port of one node to input port of another
            for (UINT nodeIndexInner = 0; nodeIndexInner < m_nodeCount; nodeIndexInner++)
            {
                const PerNodeInfo* pXMLNode = &pPipelineInfo->nodeInfo[nodeIndexInner];

                for (UINT inputPortIndex = 0; inputPortIndex < pXMLNode->inputPorts.numPorts; inputPortIndex++)
                {
                    const InputPortInfo* pInputPortInfo = &pXMLNode->inputPorts.portInfo[inputPortIndex];

                    if (FALSE == m_ppNodes[nodeIndexInner]->IsSourceBufferInputPort(inputPortIndex))
                    {
                        m_ppNodes[nodeIndexInner]->SetInputLink(inputPortIndex,
                                                           pInputPortInfo->portId,
                                                           m_ppNodes[pInputPortInfo->parentNodeIndex],
                                                           pInputPortInfo->parentOutputPortId);

                        m_ppNodes[nodeIndexInner]->SetUpLoopBackPorts(inputPortIndex);

                        /// Update access device index list for the source port based on current nodes device index list
                        /// At this point the source node which maintains the output buffer manager have the access information
                        /// required for buffer manager creation.
                        m_ppNodes[pInputPortInfo->parentNodeIndex]->AddOutputDeviceIndices(
                            pInputPortInfo->parentOutputPortId,
                            m_ppNodes[nodeIndexInner]->DeviceIndices(),
                            m_ppNodes[nodeIndexInner]->DeviceIndexCount());

                        const ImageFormat* pImageFormat = m_ppNodes[nodeIndexInner]->GetInputPortImageFormat(inputPortIndex);
                        if (NULL != pImageFormat)
                        {
                            CAMX_LOG_INFO(CamxLogGroupCore,
                                          "Topology: Link: %s:%d(outPort %d) --> (inPort %d)%s:%d using format %d",
                                          m_ppNodes[pInputPortInfo->parentNodeIndex]->Name(),
                                          m_ppNodes[pInputPortInfo->parentNodeIndex]->InstanceID(),
                                          pInputPortInfo->parentOutputPortId,
                                          pInputPortInfo->portId,
                                          m_ppNodes[nodeIndexInner]->Name(),
                                          m_ppNodes[nodeIndexInner]->InstanceID(),
                                          pImageFormat->format);
                        }
                        else
                        {
                            CAMX_LOG_ERROR(CamxLogGroupCore, "Invalid pImageFormat");
                        }
                    }
                    else
                    {
                        m_ppNodes[nodeIndexInner]->SetupSourcePort(inputPortIndex, pInputPortInfo->portId);
                    }
                }
                if (TRUE == m_ppNodes[nodeIndexInner]->IsLoopBackNode())
                {
                    m_ppNodes[nodeIndexInner]->EnableParentOutputPorts();
                }
            }
        }

        /// @todo (CAMX-1015) Look into non recursive implementation
        if (CamxResultSuccess == result)
        {
            for (UINT index = 0; index < m_nodesSinkOutPorts.numNodes; index++)
            {
                if (NULL != m_ppNodes[m_nodesSinkOutPorts.nodeIndices[index]])
                {
                    m_ppNodes[m_nodesSinkOutPorts.nodeIndices[index]]->TriggerOutputPortStreamIdSetup();
                }
            }
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "m_ppNodes or m_ppNodesFinalizeNegotiationOutput is Null");
        result = CamxResultENoMemory;
    }

    // Bypass node processing
    if (CamxResultSuccess == result)
    {
        for (UINT index = 0; index < numBypassableNodes; index++)
        {
            pBypassableNodes[index]->BypassNodeProcessing();
        }
    }

    if (CamxResultSuccess == result)
    {
        for (UINT index = 0; index < numInPlaceSinkBufferNodes; index++)
        {
            pInplaceSinkBufferNode[index]->TriggerInplaceProcessing();
        }
    }

    if (CamxResultSuccess == result)
    {
        for (UINT index = 0; index < m_nodesSinkOutPorts.numNodes; index++)
        {
            CAMX_ASSERT((NULL != m_ppNodes) && (NULL != m_ppNodes[m_nodesSinkOutPorts.nodeIndices[index]]));

            Node* pNode = m_ppNodes[m_nodesSinkOutPorts.nodeIndices[index]];

            result = pNode->TriggerBufferNegotiation();

            if (CamxResultSuccess != result)
            {
                CAMX_LOG_WARN(CamxLogGroupCore, "Unable to satisfy node input buffer requirements, retrying with NV12");
                break;
            }
        }
        if (CamxResultSuccess != result)
        {
            result = RenegotiateInputBufferRequirement(pCreateInputData, pCreateOutputData);
        }
    }

    if (CamxResultSuccess != result)
    {
        CAMX_ASSERT_ALWAYS();
        DestroyNodes();
    }
    else
    {
        UINT numInputs = 0;

        for (UINT index = 0; index < m_nodesSourceInPorts.numNodes; index++)
        {
            Node*                    pNode         = m_ppNodes[m_nodesSourceInPorts.nodeIndices[index]];
            ChiPipelineInputOptions* pInputOptions = &pCreateOutputData->pPipelineInputOptions[numInputs];

            numInputs += pNode->FillPipelineInputOptions(pInputOptions);
        }

        pCreateOutputData->numInputs = numInputs;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///  Pipeline::FinalizePipeline
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::FinalizePipeline(
    FinalizeInitializationData* pFinalizeInitializationData)
{
    CamxResult                    result              = CamxResultSuccess;
    const ImageSensorModuleData*  pSensorModuleData;
    NCSService*                   phNCSServiceHandle   = NULL;

    CAMX_ASSERT(NULL != pFinalizeInitializationData);

    // 提取出pFinalizeInitializationData中的数据，赋给管道的成员变量
    m_pHwContext            = pFinalizeInitializationData->pHwContext;
    m_hCSLSession           = pFinalizeInitializationData->pHwContext->GetCSLSession();
    m_pDeferredRequestQueue = pFinalizeInitializationData->pDeferredRequestQueue;
    m_pSession              = pFinalizeInitializationData->pSession;
    m_numSessionPipelines   = pFinalizeInitializationData->numSessionPipelines;
    m_flags.enableQTimer    = pFinalizeInitializationData->enableQTimer;

    // 获取传感器模块的数据
    pSensorModuleData   = m_pChiContext->GetHwContext()->GetImageSensorModuleData(m_cameraId);
    m_currentSensorMode = pFinalizeInitializationData->pSensorModeInfo->modeIndex;
    CAMX_LOG_VERBOSE(CamxLogGroupCore, "Current Sensor mode is %d", m_currentSensorMode);

    // Make sure metadata pools are all initialied before nodes FinalizeInitialization
    // 确保元数据池都初始化结束在节点结束初始化前
    // 等待元数据初始化
    m_pInputPool->WaitForMetadataPoolCreation();
    CAMX_ASSERT(PoolStatus::Initialized == m_pInputPool->GetPoolStatus());

    m_pInternalPool->WaitForMetadataPoolCreation();
    CAMX_ASSERT(PoolStatus::Initialized == m_pInternalPool->GetPoolStatus());

    m_pMainPool->WaitForMetadataPoolCreation();
    CAMX_ASSERT(PoolStatus::Initialized == m_pMainPool->GetPoolStatus());

    m_pEarlyMainPool->WaitForMetadataPoolCreation();
    CAMX_ASSERT(PoolStatus::Initialized == m_pEarlyMainPool->GetPoolStatus());

    pFinalizeInitializationData->pDebugDataPool->WaitForMetadataPoolCreation();
    CAMX_ASSERT(PoolStatus::Initialized == pFinalizeInitializationData->pDebugDataPool->GetPoolStatus());

    /// @todo (CAMX-1512) Metadata pools needs to be per pipeline
    m_pDebugDataPool = pFinalizeInitializationData->pDebugDataPool;

    // 若为外部传感器
    if (TRUE == pSensorModuleData->IsExternalSensor())
    {
        // 结束传感器模式初始化
        FinalizeSensorModeInitalization(pSensorModuleData);
    }

    if (FALSE == IsRealTime())
    {
        // If not realtime pipeline, need publish all necessary properties into usecase pool
        // 结束传感器模式初始化
        PublishSensorUsecaseProperties(pSensorModuleData);
    }

    // 返回指向给定相机的调整管理器的指针
    m_pTuningManager = HwEnvironment::GetInstance()->GetTuningDataManager(m_cameraId);

    if ((0 == m_nodeCount) || (NULL == m_ppNodes))
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Failed as nodes are not created ");
        result = CamxResultEFailed;
    }

    if (CamxResultSuccess == result)
    {
        // 遍历所有node，结束初始化
        for (UINT i = 0; i < m_nodeCount; i++)
        {
            result = m_ppNodes[i]->FinalizeInitialization(pFinalizeInitializationData);

            if (CamxResultSuccess != result)
            {
                CAMX_ASSERT_ALWAYS_MESSAGE("Failed to finalize init of node: %d", i);
                break;
            }
        }
    }

    if (CamxResultSuccess == result)
    {
        /// @todo (CAMX-1797) Simplify the logic involving m_ppNodesFinalizeNegotiationOutput

        // The source node will be added as the last element in the array and the sink nodes will be at the start of the array.
        // And since we need to walk-forward from the source-to-the-sink we go in reverse order here
        for (INT index = (m_finalizeNegotiationNodeCount - 1); index >= 0; index--)
        {
            if (NULL != m_ppNodesFinalizeNegotiationOutput[index])
            {
                m_ppNodesFinalizeNegotiationOutput[index]->DetermineBufferProperties();
            }
        }

        if (NULL != m_ppNodesFinalizeNegotiationOutput)
        {
            CAMX_FREE(m_ppNodesFinalizeNegotiationOutput);
            m_ppNodesFinalizeNegotiationOutput = NULL;
        }

        if (CamxResultSuccess == result)
        {
            for (UINT i = 0; i < m_nodeCount; i++)
            {
                result = m_ppNodes[i]->CreateBufferManagers();

                if (CamxResultSuccess != result)
                {
                    break;
                }
            }
        }

        if (CamxResultSuccess == result)
        {
            for (UINT i = 0; i < m_nodeCount; i++)
            {
                result = m_ppNodes[i]->NotifyPipelineCreated();

                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupCore, "Failed to initialize %s: ", m_ppNodes[i]->Name());
                    break;
                }
            }
        }

        if (CamxResultSuccess != result)
        {
            CAMX_ASSERT_ALWAYS();
            DestroyNodes();
        }
        else
        {
            result = PrepareStreamOn();
        }
    }

    if (CamxResultSuccess == result)
    {
        result = m_pHwContext->InitializeSOCDependentParams();
    }

    if (CamxResultSuccess == result)
    {
        result = Link();
        m_currentCreateStatus = PipelineCreateStatus::FINALIZED;
        CAMX_LOG_VERBOSE(CamxLogGroupCore, "pipeline:%d finalize done!", GetPipelineId());
    }

    if ((CamxResultSuccess == result) && (TRUE == IsRealTime()))
    {
        result = CSLRegisterMessageHandler(m_hCSLSession, m_hCSLLinkHandle,
            CSLMessageHandler, static_cast<VOID*>(this));
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "Failed to register message handler, error code %d", result);
        }
    }

    if (CamxResultSuccess == result)
    {
        result =  VendorTagManager::QueryVendorTagLocation("org.codeaurora.qcamera3.av_timer", "use_av_timer",
                                                           &m_vendorTagIndexAVTimer);
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupHAL,
                "Failed to find org.codeaurora.qcamera3.av_timer, resultCode=%s",
                CamxResultStrings[result]);
        }
    }

    if (CamxResultSuccess == result)
    {
        result =
            VendorTagManager::QueryVendorTagLocation("org.quic.camera.qtimer", "timestamp", &m_vendorTagIndexTimestamp);
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupHAL,
                "Failed to find org.quic.camera.qtimer.timestamp, resultCode=%s",
                CamxResultStrings[result]);
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///  Pipeline::DestroyNodes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::DestroyNodes()
{

    if (NULL != m_ppNodesFinalizeNegotiationOutput)
    {
        CAMX_FREE(m_ppNodesFinalizeNegotiationOutput);
        m_ppNodesFinalizeNegotiationOutput = NULL;
    }

    if (NULL != m_ppNodes)
    {
        for (UINT i = 0; i < m_nodeCount; i++)
        {
            if (NULL != m_ppNodes[i])
            {
                m_ppNodes[i]->Destroy();
                m_ppNodes[i] = NULL;
            }
        }

        CAMX_FREE(m_ppNodes);
        m_ppNodes   = NULL;
        m_nodeCount = 0;
    }

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::GetNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Node* Pipeline::GetNode(
    UINT nodeType,
    UINT instanceId)
{
    Node* pNode = NULL;

    for (UINT i = 0; i < m_nodeCount; i++)
    {
        if ((NULL != m_ppNodes[i]) && (m_ppNodes[i]->Type() == nodeType) && (m_ppNodes[i]->InstanceID() == instanceId))
        {
            pNode = m_ppNodes[i];
            break;
        }
    }

    return pNode;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::IsNodeExist
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL Pipeline::IsNodeExist(
    UINT nodeType)
{
    BOOL found = FALSE;

    for (UINT i = 0; i < m_nodeCount; i++)
    {
        if ((NULL != m_ppNodes[i]) && (m_ppNodes[i]->Type() == nodeType))
        {
            found = TRUE;
            break;
        }
    }

    return found;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::HasSnapshotJPEGStream
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL Pipeline::HasSnapshotJPEGStream()
{
    BOOL found = FALSE;

    for (UINT i = 0; i < m_pPipelineDescriptor->numOutputs; i++)
    {
        ChiStreamWrapper* pStreamWrapper = m_pPipelineDescriptor->outputData[i].pOutputStreamWrapper;
        Camera3Stream* pStream = pStreamWrapper->GetNativeStream();
        CAMX_ASSERT(NULL != pStream);

        if (HALPixelFormatBlob == pStream->format)
        {
            found = TRUE;
            break;
        }
    }

    return found;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::GetIntraPipelinePerFramePool
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MetadataPool* Pipeline::GetIntraPipelinePerFramePool(
    PoolType poolType,
    UINT     pipelineId)
{
    return m_pSession->GetIntraPipelinePerFramePool(poolType, pipelineId);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::GetIntraRealtimePipelineId
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::GetIntraRealtimePipelineId(
    UINT     inputPipelineId,
    UINT*    pIntraPipelineId)
{
    return m_pSession->GetIntraRealtimePipelineId(inputPipelineId, pIntraPipelineId);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::ProcessMetadataRequestIdDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::ProcessMetadataRequestIdDone(
    UINT64 requestId,
    BOOL   earlyMetadata)
{
    CamxResult      result                        = CamxResultSuccess;
    UINT            perRequestIdIndex             = requestId % MaxPerRequestInfo;
    PerRequestInfo* pPerRequestInfo               = &m_perRequestInfo[perRequestIdIndex];
    UINT64*         pFrameTimestamp               = NULL;
    UINT64          batchFrameIntervalNanoSeconds = pPerRequestInfo->batchFrameIntervalNanoSeconds;
    UINT64          captureTime                   = OsUtils::GetNanoSeconds();
    ResultsData     resultsData                   = {};
    MetadataSlot*   pMetadataSlot                 = NULL;
    MetadataSlot*   pEmptyMetadataSlot            = NULL;
    BOOL            useEmptyMeta                  = FALSE;

    if (TRUE == earlyMetadata)
    {
        pMetadataSlot    = m_pEarlyMainPool->GetSlot(requestId);
        resultsData.type = CbType::EarlyMetadata;
    }
    else
    {
        pMetadataSlot    = m_pMainPool->GetSlot(requestId);
        resultsData.type = CbType::Metadata;

        // Copy over input tags not filled by any active nodes
        result = m_pInputPool->GetSlot(requestId)->CopyDisjointTags(pMetadataSlot->GetMetadata());

        CAMX_ASSERT(CamxResultSuccess == result);

        if (CamxResultSuccess == result)
        {
            // Fill undefined default values
            if (0 == pMetadataSlot->GetMetadataCountByTag(ControlVideoStabilizationMode, FALSE))
            {
                ControlVideoStabilizationModeValues videoStabilizationMode =
                    (0 == m_videoStabilizationCaps) ? ControlVideoStabilizationModeOff : ControlVideoStabilizationModeOn;

                pMetadataSlot->SetMetadataByTag(ControlVideoStabilizationMode,
                                                static_cast<VOID*>(&videoStabilizationMode),
                                                1);
            }
        }
    }

    // Metadata Notification
    resultsData.cbPayload.metadata.pMetadata    = pMetadataSlot->GetMetadata();
    resultsData.pipelineIndex                   = m_pipelineIndex;
    resultsData.pPrivData                       = pPerRequestInfo->request.pPrivData;

    CAMX_ASSERT(NULL != pMetadataSlot);

    if (TRUE == pMetadataSlot->IsPublished(UnitType::Metadata, SensorTimestamp))
    {
        m_pMainPool->GetSlot(requestId)->GetMetadataByTag(SensorTimestamp, reinterpret_cast<VOID**>(&pFrameTimestamp));

        if (NULL != pFrameTimestamp)
        {
            captureTime = static_cast<UINT64>(*pFrameTimestamp);
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "pFrameTimestamp is NULL");
        }
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupCore, "SensorTimestamp not yet published for %llu", requestId);
    }

    if ((TRUE == m_flags.isHFRMode) && (pPerRequestInfo->request.numBatchedFrames > 1) && (FALSE == earlyMetadata))
    {
        pEmptyMetadataSlot = m_pEmptyMetaDataPool->GetSlot(requestId);
        useEmptyMeta = TRUE;
    }
    for (UINT batchIndex = 0; batchIndex < pPerRequestInfo->request.numBatchedFrames; batchIndex++)
    {
        MetadataSlot*   pActiveSlot = pMetadataSlot;
        UINT64 frameTimestamp       = captureTime + (batchFrameIntervalNanoSeconds * batchIndex);

        if ((TRUE == useEmptyMeta) && (batchIndex != (pPerRequestInfo->request.numBatchedFrames - 1)))
        {
            pActiveSlot                                 = pEmptyMetadataSlot;
        }

        resultsData.cbPayload.metadata.pMetadata        = pActiveSlot->GetMetadata();
        resultsData.cbPayload.metadata.sequenceId       = pPerRequestInfo->sequenceId[batchIndex];

        CAMX_LOG_INFO(CamxLogGroupCore,
                      "Notify %s metadata for request id %lld and sequenceId %d timestamp = %llu",
                      earlyMetadata == TRUE ? "early" : "",
                      requestId,
                      pPerRequestInfo->sequenceId[batchIndex],
                      frameTimestamp);

        // Update metadata SensorTimestamp
        if (resultsData.type == CbType::Metadata)
        {
            result = pActiveSlot->SetMetadataByTag(SensorTimestamp, reinterpret_cast<VOID*>(&frameTimestamp), 1);

            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupHAL, "SetMetadataByTag for SensorTimestamp failure %d", result);
            }
            else
            {
                UINT32 tag = SensorTimestamp;
                result = pActiveSlot->PublishMetadataList(&tag, 1);
                CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to publish SensorTimestamp");
            }
        }
        m_pSession->NotifyResult(&resultsData);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::ProcessMetadataRequestIdError
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::ProcessMetadataRequestIdError(
    UINT64        requestId)
{
    ResultsData     resultsData                   = {};
    UINT            perRequestIdIndex             = requestId % MaxPerRequestInfo;
    PerRequestInfo* pPerRequestInfo               = &m_perRequestInfo[perRequestIdIndex];

    resultsData.type                               = CbType::Error;
    resultsData.cbPayload.error.code               = MessageCodeResult;
    resultsData.pipelineIndex                      = m_pipelineIndex;
    resultsData.pPrivData                          = pPerRequestInfo->request.pPrivData;

    CAMX_LOG_INFO(CamxLogGroupCore, "Reporting metadata error for request %llu", requestId);

    for (UINT batchIndex = 0; batchIndex < pPerRequestInfo->request.numBatchedFrames; batchIndex++)
    {
        resultsData.cbPayload.error.sequenceId        = pPerRequestInfo->sequenceId[batchIndex];
        m_pSession->NotifyResult(&resultsData);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::SinkPortFenceErrorSignaled
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SinkPortFenceErrorSignaled(
    UINT          sinkPortStreamId,
    UINT32        sequenceId,
    UINT64        requestId,
    BufferHandle* phHALBuffer)
{
    ResultsData     resultsData                   = {};
    UINT            perRequestIdIndex             = requestId % MaxPerRequestInfo;
    PerRequestInfo* pPerRequestInfo               = &m_perRequestInfo[perRequestIdIndex];

    resultsData.type                               = CbType::Error;
    resultsData.cbPayload.error.code               = MessageCodeBuffer;
    resultsData.cbPayload.error.sequenceId         = sequenceId;
    resultsData.cbPayload.error.streamId           = sinkPortStreamId;
    resultsData.cbPayload.error.phBuffer           = phHALBuffer;
    resultsData.pipelineIndex                      = m_pipelineIndex;
    resultsData.pPrivData                          = pPerRequestInfo->request.pPrivData;

    CAMX_LOG_INFO(CamxLogGroupCore,
                  "Reporting stream done for stream=%d, sequenceId=%d phHALBuffer phHALBuffer %p",
                  resultsData.cbPayload.buffer.streamId,
                  sequenceId,
                  phHALBuffer);

    m_pSession->NotifyResult(&resultsData);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::SinkPortFenceSignaled
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SinkPortFenceSignaled(
    UINT           sinkPortStreamId,
    UINT32         sequenceId,
    UINT64         requestId,
    BufferHandle*  phHALBuffer,
    CSLFenceResult fenceResult)
{
    ResultsData     resultsData       = {};
    UINT            perRequestIdIndex             = requestId % MaxPerRequestInfo;
    PerRequestInfo* pPerRequestInfo               = &m_perRequestInfo[perRequestIdIndex];

    resultsData.pipelineIndex = m_pipelineIndex;
    resultsData.pPrivData = pPerRequestInfo->request.pPrivData;

    if (CSLFenceResultSuccess == fenceResult)
    {
        resultsData.type                        = CbType::Buffer;
        resultsData.cbPayload.buffer.sequenceId = sequenceId;
        resultsData.cbPayload.buffer.streamId   = sinkPortStreamId;
        resultsData.cbPayload.buffer.phBuffer   = phHALBuffer;
    }
    else
    {
        resultsData.type                       = CbType::Error;
        resultsData.cbPayload.error.code       = MessageCodeRequest;
        resultsData.cbPayload.error.sequenceId = sequenceId;
    }

    CAMX_LOG_INFO(CamxLogGroupCore,
                  "Reporting stream done for stream=%d, sequenceId=%d",
                  resultsData.cbPayload.buffer.streamId,
                  sequenceId);

    m_pSession->NotifyResult(&resultsData);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::SealDebugDataBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SealDebugDataBuffer(
    DebugDataType   dataType,
    DebugData*      pDebugData)
{
    DebugDataTag*   pTag            = NULL;
    VOID*           pDataStart      = NULL;
    SIZE_T          dataSize        = HAL3MetadataUtil::DebugDataSize(dataType);

    if (0 != dataSize)
    {
        if (sizeof(DebugDataTag) <= dataSize &&
            (HAL3MetadataUtil::DebugDataOffset(dataType) <= (pDebugData->size + sizeof(DebugDataTag))))
        {
            pDataStart = Utils::VoidPtrInc(pDebugData->pData, HAL3MetadataUtil::DebugDataOffset(dataType));

            if (DebugDataType::IPETuning == dataType)
            {
                dataSize /= DebugDataPartitionsIPE;
                for (UINT partition = 0; DebugDataPartitionsIPE > partition; partition++)
                {
                    pTag        = reinterpret_cast<DebugDataTag*>(pDataStart);
                    pTag->id    = DebugDataTagID::UnusedSpace;
                    pTag->type  = DebugDataTagType::UInt8;
                    pTag->count = static_cast<TagCount>(dataSize - sizeof(DebugDataTag));

                    pDataStart  = Utils::VoidPtrInc(pDataStart, dataSize);
                }
            }
            else
            {
                pTag        = reinterpret_cast<DebugDataTag*>(pDataStart);
                pTag->id    = DebugDataTagID::UnusedSpace;
                pTag->type  = DebugDataTagType::UInt8;
                pTag->count = static_cast<TagCount>(dataSize - sizeof(DebugDataTag));
            }
        }
        else
        {
            CAMX_LOG_WARN(CamxLogGroupCore, "Size not enough for type: %u", dataType);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::InitializeDebugDataBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::InitializeDebugDataBuffer(
    UINT64 requestId)
{
    VOID*           pBlob           = NULL;
    DebugData*      pDebugData      = NULL;
    MetadataSlot*   pMetadataSlot   = m_pDebugDataPool->GetSlot(requestId);

    pMetadataSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pBlob));

    // Clear older values from the pool, do this only for DebugData wich is not using isPublish() and direct access data
    pDebugData = reinterpret_cast<DebugData*>(
                    Utils::VoidPtrInc(pBlob,
                                      DebugDataPropertyOffsets[PropertyIDDebugDataAll & ~DriverInternalGroupMask]));

    // Use non-zero value, zero is a very common number.
    Utils::Memset(pDebugData->pData, 0xFF, pDebugData->size);

    // Seal every data block
    SealDebugDataBuffer(DebugDataType::AEC, pDebugData);
    SealDebugDataBuffer(DebugDataType::AWB, pDebugData);
    SealDebugDataBuffer(DebugDataType::AF, pDebugData);
    SealDebugDataBuffer(DebugDataType::IFETuning, pDebugData);
    SealDebugDataBuffer(DebugDataType::IPETuning, pDebugData);
    SealDebugDataBuffer(DebugDataType::BPSTuning, pDebugData);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::NonSinkPortFenceErrorSignaled
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::NonSinkPortFenceErrorSignaled(
    CSLFence* phFence,
    UINT64    requestId)
{
    m_pDeferredRequestQueue->FenceErrorSignaledCallback(
        m_pChiContext->GetHwContext(),
        m_pipelineIndex,
        *(GetCSLLink()),
        phFence,
        requestId);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::NonSinkPortFenceSignaled
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::NonSinkPortFenceSignaled(
    CSLFence* phFence,
    UINT64    requestId)
{
    m_pDeferredRequestQueue->FenceSignaledCallback(phFence, requestId);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::SendOfflineShutterNotification
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SendOfflineShutterNotification(
    UINT64  requestId,
    UINT64* pTimestamp)
{
    CSLFrameMessage message;
    message.requestID = requestId;

    if (NULL != pTimestamp)
    {
        message.timestamp = *pTimestamp;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupHAL, "timestamp should not be ZERO!");
    }

    SendShutterNotification(&message);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::SendShutterNotification
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SendShutterNotification(
    const CSLFrameMessage* pMessage)
{
    if (CamxInvalidRequestId != pMessage->requestID)
    {
        CamxResult  result      = CamxResultSuccess;
        ResultsData resultsData = {};

        resultsData.type          = CbType::Async;
        resultsData.pipelineIndex = m_pipelineIndex;

        UINT8*          pUseAVTimer                   = NULL;
        UINT            perRequestIdIndex             = pMessage->requestID % MaxPerRequestInfo;
        PerRequestInfo* pPerRequestInfo               = &m_perRequestInfo[perRequestIdIndex];
        UINT64          captureTime                   = 0;
        UINT64          frameTimestamp                = 0;
        UINT64          batchFrameIntervalNanoSeconds = 0;

        CAMX_ASSERT(pMessage->requestID == m_perRequestInfo[perRequestIdIndex].request.requestId);
        if (0 == pMessage->timestamp)
        {
            CAMX_LOG_ERROR(CamxLogGroupHAL, "pMessage->timestamp is 0, falling back to system time");
        }

        if (m_vendorTagIndexAVTimer > 0)
        {
            MetadataSlot* pMetadataSlot = m_pInputPool->GetSlot(pMessage->requestID);
            pMetadataSlot->GetMetadataByTag(m_vendorTagIndexAVTimer, reinterpret_cast<VOID**>(&pUseAVTimer));
        }

        if (((NULL != pUseAVTimer) && (1 == *pUseAVTimer)) ||
            (1 == m_flags.enableQTimer))
        {
            captureTime = pMessage->timestamp;
        }

        // Fallback option
        if (captureTime == 0)
        {
            captureTime = OsUtils::GetNanoSeconds();
        }

        // There is one CSL SOF notification per batch, so we need to update batchFrameInterval for HFR base on the FPS,
        // and append the interval for each frames.
        if (pPerRequestInfo->request.numBatchedFrames > 1)
        {
            /// @todo (CAMX-1015) - Check if we don't need to query fps every single time
            MetadataSlot*   pUsecaseMetadataSlot = m_pUsecasePool->GetSlot(0);
            CAMX_ASSERT(NULL != pUsecaseMetadataSlot);

            if (TRUE == pUsecaseMetadataSlot->IsPublished(UnitType::Property, PropertyIDUsecaseFPS))
            {
                UINT                 FPS   = 0;
                UsecasePropertyBlob* pBlob = NULL;

                pUsecaseMetadataSlot->ReadLock();
                pUsecaseMetadataSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pBlob));
                CAMX_ASSERT(NULL != pBlob);

                FPS = pBlob->mUsecaseFPS;
                CAMX_ASSERT(0 != FPS);

                pUsecaseMetadataSlot->Unlock();

                batchFrameIntervalNanoSeconds = NanoSecondsPerSecond / FPS;
            }
        }

        pPerRequestInfo->batchFrameIntervalNanoSeconds = batchFrameIntervalNanoSeconds;

        // Update metadata SensorTimestamp
        MetadataSlot*   pMainMetadataSlot  = m_pMainPool->GetSlot(pMessage->requestID);
        MetadataSlot*   pEarlyMetadataSlot = m_pEarlyMainPool->GetSlot(pMessage->requestID);
        CAMX_ASSERT(NULL != pMainMetadataSlot);

        result =  pMainMetadataSlot->SetMetadataByTag(SensorTimestamp, reinterpret_cast<VOID*>(&captureTime), 1);

        if (CamxResultSuccess == result)
        {
            result = pEarlyMetadataSlot->SetMetadataByTag(SensorTimestamp, reinterpret_cast<VOID*>(&captureTime), 1);
        }

        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupHAL, "SetMetadataByTag for SensorTimestamp failure %d", result);
        }
        else
        {
            UINT32 tag = SensorTimestamp;
            result = pMainMetadataSlot->PublishMetadataList(&tag, 1);
            CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to publish SensorTimestamp");
        }

        for (UINT batchIndex = 0; batchIndex < pPerRequestInfo->request.numBatchedFrames; batchIndex++)
        {
            frameTimestamp = captureTime + (batchFrameIntervalNanoSeconds * batchIndex);
            resultsData.cbPayload.async.sequenceId = pPerRequestInfo->sequenceId[batchIndex];
            resultsData.cbPayload.async.timestamp  = frameTimestamp;
            resultsData.pPrivData                  = pPerRequestInfo->request.pPrivData;
            m_pSession->NotifyResult(&resultsData);
        }

        BOOL nodesOutstanding =
            CamxAtomicCompareExchangeU(&m_perRequestInfo[pMessage->requestID % MaxPerRequestInfo].aMetadataReady, 0, 1);

        if (FALSE == nodesOutstanding)
        {
            ProcessMetadataRequestIdDone(pMessage->requestID, FALSE);
        }

    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::SendErrorNotification
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SendErrorNotification(
    const CSLErrorMessage* pMessage)
{
    ResultsData resultsData = {};

    resultsData.type          = CbType::Error;
    resultsData.pipelineIndex = m_pipelineIndex;

    switch (pMessage->errorType)
    {
        case CSLErrorMessageCodeDevice:
            resultsData.cbPayload.error.code = MessageCodeDevice;
            break;
        case CSLErrorMessageCodeRequest:
            resultsData.cbPayload.error.code = MessageCodeRequest;
            break;
        case CSLErrorMessageCodeBuffer:
            resultsData.cbPayload.error.code = MessageCodeBuffer;
            break;
        default:
            CAMX_LOG_ERROR(CamxLogGroupCore, "Unexpected error type");
            break;
    }

    UINT            perRequestIdIndex = pMessage->requestID % MaxPerRequestInfo;
    PerRequestInfo* pPerRequestInfo   = &m_perRequestInfo[perRequestIdIndex];

    // No request frame is applicable to the error
    if (CamxInvalidRequestId == pMessage->requestID)
    {
        // CSLErrorMessageCodeRequest CSLErrorMessageCodeBuffer should correspond to a valid request id.
        // Only CSLErrorMessageCodeDevice is not related to per request, and we should set the notification frame number to 0
        CAMX_ASSERT(CSLErrorMessageCodeDevice == pMessage->errorType);

        resultsData.cbPayload.error.sequenceId = 0;
        resultsData.pPrivData                  = pPerRequestInfo->request.pPrivData;
        m_pSession->NotifyResult(&resultsData);
    }
    else
    {
        CAMX_ASSERT(pMessage->requestID == pPerRequestInfo->request.requestId);

        for (UINT batchIndex = 0; batchIndex < pPerRequestInfo->request.numBatchedFrames; batchIndex++)
        {
            resultsData.cbPayload.error.sequenceId = pPerRequestInfo->sequenceId[batchIndex];
            resultsData.pPrivData                  = pPerRequestInfo->request.pPrivData;

            CAMX_LOG_VERBOSE(CamxLogGroupCore,
                "SendErrorNotification message request id %lld, sequence ID %d",
                pMessage->requestID,
                resultsData.cbPayload.error.sequenceId);

            m_pSession->NotifyResult(&resultsData);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::SendSOFNotification
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SendSOFNotification(
    const CSLFrameMessage* pMessage)
{
    ResultsData resultsData = {};

    resultsData.type                                = CbType::SOF;
    resultsData.pipelineIndex                       = m_pipelineIndex;
    resultsData.cbPayload.sof.frameNum              = static_cast<UINT32>(pMessage->frameCount);
    resultsData.cbPayload.sof.timestamp             = pMessage->timestamp;
    resultsData.cbPayload.sof.bIsSequenceIdValid    = (CamxInvalidRequestId != pMessage->requestID);
    if (resultsData.cbPayload.sof.bIsSequenceIdValid)
    {
        UINT            perRequestIdIndex    = pMessage->requestID % MaxPerRequestInfo;
        PerRequestInfo* pPerRequestInfo      = &m_perRequestInfo[perRequestIdIndex];
        resultsData.cbPayload.sof.sequenceId = pPerRequestInfo->sequenceId[0];
    }
    m_pSession->NotifyResult(&resultsData);

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::CSLMessageHandler
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::CSLMessageHandler(
    VOID*        pUserData,
    CSLMessage*  pMessage)
{
    CAMX_ASSERT((NULL != pMessage) && (NULL != pUserData));

    Pipeline*         pPipeline         = static_cast<Pipeline*>(pUserData);
    CamxResult        result            = CamxResultSuccess;
    UINT64            qtimerTimestamp   = 0;
    UINT16            requestIdIndex    = 0;
    MetadataPool*     pMainPool         = NULL;
    MetadataSlot*     pMainMetadataSlot = NULL;
    MainPropertyBlob* pMainPropertyBlob = NULL;

    switch (pMessage->type)
    {
        case CSLMessageTypeFrame:

            qtimerTimestamp = pMessage->message.frameMessage.timestamp;

            // The requestID in CSL message is CSL sync ID and so need to get the real pipeline request ID
            requestIdIndex = (pMessage->message.frameMessage.requestID % (MaxPerRequestInfo * MaxBatchedFrames));
            if (CamxInvalidRequestId != pMessage->message.frameMessage.requestID)
            {
                pMessage->message.frameMessage.requestID = pPipeline->m_CSLSyncIDToRequestId[requestIdIndex];
            }

            // The timestamp reported by KMD is Qtimer timestamp. For framework, we need to convert it to
            // MONOTONIC system timestamp
            pMessage->message.frameMessage.timestamp =
                pPipeline->ConvertQTimerTimestampToMonotonicSystemTimestamp(pMessage->message.frameMessage.requestID,
                                                                            pMessage->message.frameMessage.timestamp);

            CAMX_LOG_INFO(CamxLogGroupCore,
                "frameMessage.requestID=%lld, frameMessage.frameCount=%lld, frameMessage.timestamp=%lld, monoTimestamp=%lld",
                pMessage->message.frameMessage.requestID,
                pMessage->message.frameMessage.frameCount,
                qtimerTimestamp,
                pMessage->message.frameMessage.timestamp);

            // We publish the QTimer timestamp through vendor tag and Monotonic system timestamp through
            // shutter notification
            if (CamxInvalidRequestId != pMessage->message.frameMessage.requestID)
            {
                pMainPool = pPipeline->GetPerFramePool(PoolType::PerFrameResult);
                CAMX_ASSERT(NULL != pMainPool);
                pMainMetadataSlot = pMainPool->GetSlot(pMessage->message.frameMessage.requestID);
                CAMX_ASSERT(NULL != pMainMetadataSlot);
                if (CamxResultSuccess == result)
                {
                    pMainMetadataSlot->SetMetadataByTag(pPipeline->m_vendorTagIndexTimestamp, &qtimerTimestamp, 1);
                    pMainMetadataSlot->WriteLock();
                    pMainMetadataSlot->PublishMetadataList(&(pPipeline->m_vendorTagIndexTimestamp), 1);
                    pMainMetadataSlot->Unlock();
                }
            }

            pPipeline->SendSOFNotification(&(pMessage->message.frameMessage));

            for (UINT i = 0; i < pPipeline->m_nodeCount; i++)
            {
                if (NULL != pPipeline->m_ppNodes[i])
                {
                    pPipeline->m_ppNodes[i]->NotifyNodeCSLMessage(pMessage);
                }
            }

            if ((CamxInvalidRequestId != pMessage->message.frameMessage.requestID) && (NULL!= pMainMetadataSlot))
            {
                pMainMetadataSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pMainPropertyBlob));

                if (NULL != pMainPropertyBlob &&
                    TRUE == pMainMetadataSlot->IsPublished(UnitType::Property, PropertyIDSensorExposureStartTime))
                {
                    pMainMetadataSlot->ReadLock();
                    pMessage->message.frameMessage.timestamp = pMainPropertyBlob->mSensorExposureStartTime;
                    pMainMetadataSlot->Unlock();
                }
            }

            pPipeline->SendShutterNotification(&(pMessage->message.frameMessage));

            break;
        case CSLMessageTypeError:

            // The requestID in CSL message is CSL sync ID and so need to get the real pipeline request ID
            requestIdIndex = (pMessage->message.frameMessage.requestID % (MaxPerRequestInfo * MaxBatchedFrames));
            pMessage->message.frameMessage.requestID = pPipeline->m_CSLSyncIDToRequestId[requestIdIndex];

            CAMX_LOG_ERROR(CamxLogGroupCore,
                "request id %lld, error type = %d, device handle = %d, resource index = %d",
                pMessage->message.errorMessage.requestID,
                pMessage->message.errorMessage.errorType,
                pMessage->message.errorMessage.hDevice,
                pMessage->message.errorMessage.resourceIndex);

            pPipeline->SendErrorNotification(&(pMessage->message.errorMessage));
            break;

        default:
            CAMX_LOG_ERROR(CamxLogGroupCore, "Unexpected CSL message type");
            break;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::RenegotiateInputBufferRequirement
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Pipeline::RenegotiateInputBufferRequirement(
    PipelineCreateInputData*  pCreateInputData,
    PipelineCreateOutputData* pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateInputData);
    CAMX_UNREFERENCED_PARAM(pCreateOutputData);

    /// @todo (CAMX-1797) Upcomming CHI override changes will change this
    ///                   logic as it should loop through valid formats
    ///                   defined in the topology xml and re-walk
    CamxResult result = CamxResultSuccess;

    for (UINT i = 0; i < m_nodeCount; i++)
    {
        if (NULL != m_ppNodes[i])
        {
            CAMX_LOG_VERBOSE(CamxLogGroupCore,
                             "ResetBufferNegotiationData node %d instance %d i %d",
                             m_ppNodes[i]->Type(),
                             m_ppNodes[i]->InstanceID(),
                             i);

            m_ppNodes[i]->ResetBufferNegotiationData();
        }
    }

    for (UINT index = 0; index < m_finalizeNegotiationNodeCount; index++)
    {
        m_ppNodesFinalizeNegotiationOutput[index] = NULL;
    }

    m_finalizeNegotiationNodeCount = 0;

    for (UINT nodeIndex = 0; nodeIndex < m_nodesSinkOutPorts.numNodes; nodeIndex++)
    {
        CAMX_ASSERT((NULL != m_ppNodes) && (NULL != m_ppNodes[m_nodesSinkOutPorts.nodeIndices[nodeIndex]]));

        Node* pNode = m_ppNodes[m_nodesSinkOutPorts.nodeIndices[nodeIndex]];

        pNode->SwitchNodeOutputFormat(Format::YUV420NV12);

        result = pNode->TriggerBufferNegotiation();

        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "Unable to satisfy node input buffer requirements");
            break;
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::SetNumBatchedFrames
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::SetNumBatchedFrames(
    UINT usecaseNumBatchedFrames,
    UINT usecaseFPSValue)
{
    // NumBatchedFrames of 1 essentially means batching is disabled for the usecase
    UINT numBatchedFrames   = usecaseNumBatchedFrames;
    UINT fpsValue           = usecaseFPSValue;

    // set it to default values to handle if m_usecaseNumBatchedFrames is 0
    if (numBatchedFrames == 0)
    {
        numBatchedFrames = 1;
    }

    // set it to default values to handle if m_usecaseNumBatchedFrames is 0
    if (fpsValue == 0)
    {
        fpsValue = 30;
    }

    MetadataSlot*        pSlot = m_pUsecasePool->GetSlot(0);
    UsecasePropertyBlob* pBlob = NULL;

    CAMX_ASSERT(NULL != pSlot);

    pSlot->WriteLock();
    pSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pBlob));

    CAMX_ASSERT(NULL != pBlob);

    pBlob->mUsecaseBatch = numBatchedFrames;
    pSlot->PublishProperty(PropertyIDUsecaseBatch);

    pBlob->mUsecaseFPS = fpsValue;
    pSlot->PublishProperty(PropertyIDUsecaseFPS);

    pSlot->Unlock();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::GetNumBatchedFrames
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT Pipeline::GetNumBatchedFrames()
{
    MetadataSlot*        pSlot            = m_pUsecasePool->GetSlot(0);
    UsecasePropertyBlob* pBlob            = NULL;
    UINT                 numBatchedFrames = 1;

    CAMX_ASSERT(NULL != pSlot);

    pSlot->ReadLock();
    pSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pBlob));

    CAMX_ASSERT(NULL != pBlob);

    numBatchedFrames = pBlob->mUsecaseBatch;

    pSlot->Unlock();

    return numBatchedFrames;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::GetFPSValue
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT Pipeline::GetFPSValue()
{
    MetadataSlot*        pSlot    = m_pUsecasePool->GetSlot(0);
    UsecasePropertyBlob* pBlob    = NULL;
    UINT                 fpsValue = 30;

    CAMX_ASSERT(NULL != pSlot);

    pSlot->ReadLock();
    pSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pBlob));

    CAMX_ASSERT(NULL != pBlob);

    fpsValue = pBlob->mUsecaseFPS;

    pSlot->Unlock();

    return fpsValue;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::FinalizeSensorModeInitalization
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::FinalizeSensorModeInitalization(
    const ImageSensorModuleData* pSensorModuleData)
{
    MetadataPool*           pPerUsecasePool = m_pUsecasePool;
    MetadataSlot*           pPerUsecaseSlot = NULL;
    UsecasePropertyBlob*    pPerUsecaseBlob = NULL;
    UINT16                  cameraId        = 0;
    UINT32                  CSIPHYSlotInfo  = 0;
    BOOL                    comboMode       = FALSE;

    // If the pool creation failed we will *never* come here
    CAMX_ASSERT(NULL != pPerUsecasePool);

    if (CamxResultSuccess == pSensorModuleData->GetCameraId(&cameraId))
    {
        // For external sensor, upper 8 bit store the CSIPHY slot info.
        // 对于外部传感器，高8位存储CSIPHY时隙信息。
        // Camera Serial Interface
        CSIPHYSlotInfo = (cameraId & 0xFF00) >> 8;
        comboMode      = pSensorModuleData->GetCSIInfo()->isComboMode;

        CAMX_LOG_INFO(CamxLogGroupCore,
                      "Sensor Module data cameraId:%d CSIPHYSlotInfo:%d comboMode:%d",
                      cameraId,
                      CSIPHYSlotInfo,
                      comboMode);

        // 获取m_pSlots的第一个元素
        pPerUsecaseSlot = pPerUsecasePool->GetSlot(0);
        CAMX_ASSERT(NULL != pPerUsecaseSlot);

        pPerUsecaseSlot->WriteLock();
        // 获取用例的属性 binary large object
        pPerUsecaseSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pPerUsecaseBlob));

        // This can *never* happen because if there is no blob metadata pool creation should have failed
        CAMX_ASSERT(NULL != pPerUsecaseBlob);

        UsecaseSensorModes* pSensorModeData = &pPerUsecaseBlob->mUsecaseSensorModes;

        // 从ImageSensorData中获取分辨率信息用以填充传感器模式数据
        pSensorModuleData->GetSensorDataObject()->PopulateSensorModeData(pSensorModeData,
                                                                         CSIPHYSlotInfo,
                                                                         comboMode);

        // 立即通知
        pPerUsecaseSlot->PublishProperty(PropertyIDUsecaseSensorModes);

        pPerUsecaseSlot->Unlock();
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Failed to get CSIPHYSlotInfo from sensor driver binary");
    }
    // 从MetadataSlot中获取blob传递给pMainPropertyBlob并立即通知
    PublishSensorModeInformation(FirstValidRequestId);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::PublishOutputDimensions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::PublishOutputDimensions()
{
    MetadataPool*           pPerUsecasePool = m_pUsecasePool;
    MetadataSlot*           pPerUsecaseSlot = NULL;
    UsecasePropertyBlob*    pPerUsecaseBlob = NULL;
    Camera3Stream*          pStream         = NULL;
    UINT32                  previewMetaTag  = 0;
    UINT32                  videoMetaTag    = 0;
    UINT32                  snapshotMetaTag = 0;
    UINT32                  depthMetaTag    = 0;
    CamxResult              result          = CamxResultSuccess;

    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera.streamDimension",
                                                      "preview",
                                                      &previewMetaTag);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to get vendor tag: org.quic.camera.streamDimension.preview");

    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera.streamDimension",
                                                      "video",
                                                      &videoMetaTag);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to get vendor tag: org.quic.camera.streamDimension.video");

    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera.streamDimension",
                                                      "snapshot",
                                                      &snapshotMetaTag);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to get vendor tag: org.quic.camera.streamDimension.snapshot");

    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera.streamDimension",
                                                      "depth",
                                                      &depthMetaTag);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to get vendor tag: org.quic.camera.streamDimension.depth");

    // If the pool creation failed we will *never* come here
    CAMX_ASSERT(NULL != pPerUsecasePool);
    CAMX_ASSERT(NULL != m_pPipelineDescriptor);

    pPerUsecaseSlot = pPerUsecasePool->GetSlot(0);
    CAMX_ASSERT(NULL != pPerUsecaseSlot);

    pPerUsecaseSlot->WriteLock();
    pPerUsecaseSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pPerUsecaseBlob));

    // This can *never* happen because if there is no blob metadata pool creation should have failed
    CAMX_ASSERT(NULL != pPerUsecaseBlob);
    pPerUsecaseBlob->mUsecasePipelineOutputDimensions.numberOutputs = m_pPipelineDescriptor->numOutputs;

    for (UINT i = 0; i < m_pPipelineDescriptor->numOutputs; i++)
    {
        ChiStreamWrapper* pStreamWrapper = m_pPipelineDescriptor->outputData[i].pOutputStreamWrapper;
        pStream = pStreamWrapper->GetNativeStream();
        CAMX_ASSERT(NULL != pStream);

        CHIPIPELINEOUTPUTDIMENSION* pOutputDimension = &pPerUsecaseBlob->mUsecasePipelineOutputDimensions.dimensions[i];
        pOutputDimension->outputDimension.width      = pStream->width;
        pOutputDimension->outputDimension.height     = pStream->height;

        if (pStreamWrapper->IsPreviewStream())
        {
            pOutputDimension->outputType = ChiPipelineOutputPreview;
            pPerUsecaseSlot->SetMetadataByTag(previewMetaTag,
                                              static_cast<VOID*>(&pOutputDimension->outputDimension),
                                              sizeof(ChiBufferDimension));
            pPerUsecaseSlot->PublishMetadataList(&previewMetaTag, 1);
        }
        else if (pStreamWrapper->IsVideoStream())
        {
            pOutputDimension->outputType = ChiPipelineOutputVideo;
            pPerUsecaseSlot->SetMetadataByTag(videoMetaTag,
                                              static_cast<VOID*>(&pOutputDimension->outputDimension),
                                              sizeof(ChiBufferDimension));
            pPerUsecaseSlot->PublishMetadataList(&videoMetaTag, 1);
        }
        else if (pStreamWrapper->IsSnapshotStream())
        {
            pOutputDimension->outputType = ChiPipelineOutputSnapshot;
            pPerUsecaseSlot->SetMetadataByTag(snapshotMetaTag,
                                              static_cast<VOID*>(&pOutputDimension->outputDimension),
                                              sizeof(ChiBufferDimension));
            pPerUsecaseSlot->PublishMetadataList(&snapshotMetaTag, 1);
        }
        else if (pStreamWrapper->IsDepthStream())
        {
            pOutputDimension->outputType = ChiPipelineOutputDepth;
            depthMetaTag |= UsecaseMetadataSectionMask;
            pPerUsecaseSlot->SetMetadataByTag(depthMetaTag,
                                              static_cast<VOID*>(&pOutputDimension->outputDimension),
                                              sizeof(ChiBufferDimension));
            pPerUsecaseSlot->PublishMetadataList(&depthMetaTag, 1);
        }
        else
        {
            pOutputDimension->outputType = ChiPipelineOutputDefault;
        }
        CAMX_LOG_VERBOSE(CamxLogGroupCore, "Publish w %d h %d type %d, stream type %d gralloc usage 0x%x",
                         pOutputDimension->outputDimension.width,
                         pOutputDimension->outputDimension.height,
                         pOutputDimension->outputType,
                         pStream->streamType,
                         pStream->grallocUsage);
    }

    pPerUsecaseSlot->PublishProperty(PropertyIDUsecasePipelineOutputDimensions);
    pPerUsecaseSlot->Unlock();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::PublishTargetFPS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::PublishTargetFPS()
{
    MetadataPool*           pPerUsecasePool  = m_pUsecasePool;
    MetadataSlot*           pPerUsecaseSlot  = NULL;
    UsecasePropertyBlob*    pPerUsecaseBlob  = NULL;
    UINT32                  targetFPSMetaTag = 0;
    CamxResult              result           = CamxResultSuccess;
    UINT32                  targetFPS        = 0;

    result = VendorTagManager::QueryVendorTagLocation("org.codeaurora.qcamera3.sensor_meta_data",
                                                      "targetFPS",
                                                      &targetFPSMetaTag);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result,
                        "Failed to get vendor tag: org.codeaurora.qcamera3.sensor_meta_data.targetFPS");

    // If the pool creation failed we will *never* come here
    CAMX_ASSERT(NULL != pPerUsecasePool);
    CAMX_ASSERT(NULL != m_pPipelineDescriptor);

    pPerUsecaseSlot = pPerUsecasePool->GetSlot(0);
    CAMX_ASSERT(NULL != pPerUsecaseSlot);

    for (UINT i = 0; i < m_pPipelineDescriptor->numInputs; i++)
    {
        targetFPS = Utils::MaxUINT32(targetFPS, m_pPipelineDescriptor->inputData[i].sensorInfo.sensorMode.frameRate);
    }

    pPerUsecaseSlot->SetMetadataByTag(targetFPSMetaTag, static_cast<VOID*>(&targetFPS), 1);
    pPerUsecaseSlot->PublishMetadataList(&targetFPSMetaTag, 1);
    CAMX_LOG_VERBOSE(CamxLogGroupCore, "Publishing target FPS %u", targetFPS);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::PublishSensorModeInformation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::PublishSensorModeInformation(
    UINT64 requestId)
{
    MainPropertyBlob*  pMainPropertyBlob = NULL;
    MetadataSlot*      pMetadataSlot     = m_pMainPool->GetSlot(requestId);

    pMetadataSlot->WriteLock();
    // 从MetadataSlot中获取blob传递给pMainPropertyBlob
    pMetadataSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pMainPropertyBlob));
    if (NULL != pMainPropertyBlob)
    {
        pMainPropertyBlob->mSensorCurrentMode = m_currentSensorMode;
        pMetadataSlot->PublishProperty(PropertyIDSensorCurrentMode);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "pMainPropertyBlob is NULL");
    }

    pMetadataSlot->Unlock();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::PublishSensorUsecaseProperties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::PublishSensorUsecaseProperties(
    const ImageSensorModuleData* pSensorModuleData)
{
    // publish PropertyIDUsecaseSensorModes
    FinalizeSensorModeInitalization(pSensorModuleData);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::ConvertQTimerTimestampToMonotonicSystemTimestamp
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT64 Pipeline::ConvertQTimerTimestampToMonotonicSystemTimestamp(
    UINT64 requestId,
    UINT64 qTimerTimestamp)
{
    UINT64 currentTimestamp;
    if (1 == requestId || m_lastMonoTimestamp == 0)
    {
        currentTimestamp = OsUtils::GetNanoSeconds();
    }
    else
    {
        // At 19.2MHz, overflow in this 64-bit unsigned value happens once every 30,465 years so we do not worry about it here.
        currentTimestamp = (qTimerTimestamp - m_lastQTimerTimestamp) + m_lastMonoTimestamp;
    }
    m_lastMonoTimestamp   = currentTimestamp;
    m_lastQTimerTimestamp = qTimerTimestamp;

    return currentTimestamp;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::DumpState
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::DumpState(
    INT     fd,
    UINT32  indent)
{
    CAMX_LOG_TO_FILE(fd, indent, "Pipeline state:");
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_isStreamedOn: %d", m_isStreamedOn);
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_currentSensorMode: %d", m_currentSensorMode);
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_sensorSyncMode: %d", m_sensorSyncMode);
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_flags: %d", m_flags);
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_pipelineIndex: %d", m_pipelineIndex);
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_lastRequestActiveStreamIdMask: %d", m_lastRequestActiveStreamIdMask);
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_hCSLLinkHandle: %d", m_hCSLLinkHandle);
    CAMX_LOG_TO_FILE(fd, indent + 1, "m_nodeCount: %d", m_nodeCount);
    CAMX_LOG_TO_FILE(fd, indent, "+------------------------------------------------------------------+");
    CAMX_LOG_TO_FILE(fd, indent, "Graph:");

    CAMX_LOG_TO_FILE(fd, indent + 1, "Nodes + ports:");
    for (UINT i = 0; i < m_nodeCount; i++)
    {
        m_ppNodes[i]->DumpNodeInfo(fd, indent + 2);
    }
    CAMX_LOG_TO_FILE(fd, indent + 1, "Links:");
    for (UINT i = 0; i < m_nodeCount; i++)
    {
        m_ppNodes[i]->DumpLinkInfo(fd, indent + 2);
    }
    CAMX_LOG_TO_FILE(fd, indent, "+------------------------------------------------------------------+");
    CAMX_LOG_TO_FILE(fd, indent, "Per request info:");
    for (UINT i = 0; i < MaxPerRequestInfo; i++)
    {
        CAMX_LOG_TO_FILE(fd, indent + 1, "Request Id: %lld", m_perRequestInfo[i].request.requestId);
        CAMX_LOG_TO_FILE(fd, indent + 3, "numNodes: %d", m_nodeCount);
        CAMX_LOG_TO_FILE(fd, indent + 3, "numNodesRequestIdDone: %d", m_perRequestInfo[i].numNodesRequestIdDone);
        CAMX_LOG_TO_FILE(fd, indent + 3, "numNodesMetadataDone: %d", m_perRequestInfo[i].numNodesMetadataDone);
        CAMX_LOG_TO_FILE(fd, indent + 3, "numNodesConfigDone: %d", m_perRequestInfo[i].numNodesConfigDone);
        CAMX_LOG_TO_FILE(fd, indent + 3, "isSensorTimestampValid: %d", m_perRequestInfo[i].isSensorTimestampValid);
        CAMX_LOG_TO_FILE(fd, indent + 3, "batchFrameIntervalNanoSeconds: %lld",
            m_perRequestInfo[i].batchFrameIntervalNanoSeconds);
        CAMX_LOG_TO_FILE(fd, indent + 3, "numBatchedFrames: %d", m_perRequestInfo[i].request.numBatchedFrames);
        for (UINT j = 0; j < m_perRequestInfo[i].request.numBatchedFrames; j++)
        {
            CAMX_LOG_TO_FILE(fd, indent + 5, "sequenceId[%d]: %d", j, m_perRequestInfo[i].sequenceId[j]);
        }
        for (UINT j = 0; j < m_nodeCount; j++)
        {
            m_ppNodes[j]->DumpState(fd, indent + 3, m_perRequestInfo[i].request.requestId);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::DetermineFrameDelay
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT32 Pipeline::DetermineFrameDelay()
{
    UINT32        metaTag      = 0;
    CamxResult    result       = CamxResultSuccess;
    VOID*         pData        = NULL;
    MetadataPool* pUsecasePool = NULL;
    MetadataSlot* pSlot        = NULL;
    UINT32        frameDelay   = 0;
    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera.eislookahead", "FrameDelay", &metaTag);
    if (CamxResultSuccess == result)
    {
        pUsecasePool = GetPerFramePool(PoolType::PerUsecase);
        CAMX_ASSERT(NULL != pUsecasePool);

        pSlot        = pUsecasePool->GetSlot(0);
        CAMX_ASSERT(NULL != pSlot);

        pSlot->ReadLock();
        if (TRUE == pSlot->IsPublished(UnitType::Metadata, metaTag))
        {
            pSlot->GetMetadataByTag(metaTag, &pData);
        }
        pSlot->Unlock();

        if (NULL != pData)
        {
            frameDelay = *static_cast<UINT32*>(pData);
            CAMX_LOG_VERBOSE(CamxLogGroupCore, "Pipeline frame delay is %u", frameDelay);
        }
    }

    return frameDelay;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pipeline::GetRequestQueueDepth
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT32 Pipeline::GetRequestQueueDepth()
{
    UINT32 requestQueueDepth = DefaultRequestQueueDepth + DetermineFrameDelay();

    if (RequestQueueDepth < requestQueueDepth)
    {
        CAMX_LOG_WARN(CamxLogGroupCore,
                      "Current Request Queue Depth %d is larger than our max %d, capping depth to the max",
                      requestQueueDepth,
                      RequestQueueDepth);
        requestQueueDepth = RequestQueueDepth;
    }

    return requestQueueDepth;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Pipeline::Flush
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Pipeline::Flush()
{
    m_pConfigDoneLock->Lock();
    m_configDoneCount = 0;
    m_pConfigDoneLock->Unlock();
}

CAMX_NAMESPACE_END
